package com.flying.common.util;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;

import com.flying.framework.data.Data;

@SuppressWarnings("unchecked")
public abstract class BeanUtils {
	public final static  Object[] METHOD_EMPTY_PARAMS = new Object[]{};
	
	public static void setProperty(Object target, String propertyName, Object value) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		if(target == null || StringUtils.isEmpty(propertyName)) return;
		if(!hasProperty(target.getClass(), propertyName)) return;
		if(target instanceof Map) {
			((Map)target).put(propertyName, value);
			return;
		}
		PropertyDescriptor descriptor = getPropertyDescriptor(target.getClass(), propertyName);
		if(descriptor.getWriteMethod() != null) {
			PropertyUtils.setProperty(target, propertyName, value);
		} else {
			Method[] methods = target.getClass().getMethods();
			String mName = "set"+propertyName.substring(0,1).toUpperCase()+propertyName.substring(1);
			for(Method m:methods){
				if(mName.equals(m.getName())){
					m.invoke(target, new Object[]{value});
					return;
				}
			}
		}
	}
	
	@SuppressWarnings("deprecation")
	public static void setProperty(Object target,Class propertyType, String propertyName, String value) throws NumberFormatException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
		if(propertyType == int.class || propertyType == Integer.class){
			setProperty(target, propertyName, new Integer(value ==null?0:Integer.parseInt(value)));
		} else if(propertyType == long.class || propertyType == Long.class){
			setProperty(target, propertyName, new Long(value == null?0:Long.parseLong(value)));
		} else if(propertyType == double.class || propertyType == Double.class){
			setProperty(target, propertyName, new Double(value == null?0:Double.parseDouble(value)));
		} else if(propertyType == float.class || propertyType == Float.class){
			setProperty(target, propertyName, new Float(value == null?0:Float.parseFloat(value)));
		} else if(propertyType == boolean.class || propertyType == Boolean.class){
			setProperty(target, propertyName, new Boolean(StringUtils.equalsIgnoreCase("true", value)));
		} else if(propertyType == java.util.Date.class){
			setProperty(target, propertyName, DateUtils.parseDate(value));
		} else if(propertyType == java.sql.Date.class){
			java.util.Date d = DateUtils.parseDate(value);
			if(d != null){
				setProperty(target, propertyName, new java.sql.Date(d.getTime()));
			} else {
				setProperty(target, propertyName, null);
			}
		} else if(propertyType == java.lang.String.class) {
			setProperty(target, propertyName, value);
		}
	}
	
	public static Object getProperty(Object obj, String property) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{
		if(obj == null || StringUtils.isEmpty(property)) return null;

		if(obj instanceof Map) {
			return ((Map)obj).get(property);
		} else {
			return PropertyUtils.getProperty(obj, property);
		}
	}
	
	public static boolean hasProperty(Class cls, String propertyName){
		PropertyDescriptor[] descriptors = getPropertyDescriptors(cls);
		for(int i=0; i< descriptors.length; i++){
			if(StringUtils.equalsIgnoreCase(descriptors[i].getName(), propertyName)){
				return true;
			}
		}
		return false;
	}
	
	public static PropertyDescriptor[] getPropertyDescriptors(Class cls){
		return org.apache.commons.beanutils.PropertyUtils.getPropertyDescriptors(cls);
	}
	
	@SuppressWarnings("rawtypes")
	public static PropertyDescriptor getPropertyDescriptor(Class cls, String property){
		if(StringUtils.isEmpty(property)) return null;
		PropertyDescriptor[] descriptors = getPropertyDescriptors(cls);
		for(PropertyDescriptor descriptor: descriptors){
			if(StringUtils.equals(property, descriptor.getName())){
				return descriptor;
			}
		}
		return null;
	}
	
	public static <T> T restore(Map<String, Serializable> map,Class<T> clasz) throws Exception {
		T o = clasz.newInstance();
		for(String key:map.keySet()){
			Object v = map.get(key);
			BeanUtils.setProperty(o, key, v);
		}
		return o;
	}
	
	public static List<Field> getAllFields(Class<?> clazz) {
		List<Field> fields = Utils.newArrayList();
		while(clazz != null && clazz != Data.class) {
			var fs = clazz.getDeclaredFields();
			for(Field f: fs) {
				if(Modifier.isStatic(f.getModifiers())){
					continue;
				}
				if(fields.stream().anyMatch(x -> x.getName().equals(f.getName()))) {
					continue;
				}
				fields.add(f);
			}
			clazz = clazz.getSuperclass();
		}
		return fields;
	}
}
